package com.springsummer.information.green.beans;



import com.springsummer.information.green.annotation.AutoValue;
import com.springsummer.information.green.annotation.Autowired;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class InjectionMetadata {

    private Class<?> targetClass;

    private Collection<InjectionElement> injectionElements;

    private List<String> elementMemberNames;


    public InjectionMetadata() {
        this.elementMemberNames = new ArrayList<>();
    }

    public InjectionMetadata(Class<?> targetClass, Collection<InjectionElement> injectionElements) {
        this.targetClass = targetClass;
        this.injectionElements = injectionElements;
        this.elementMemberNames = new ArrayList<>();
    }

    public InjectionMetadata(Class<?> targetClass, Collection<InjectionElement> injectionElements, List<String> elementMemberNames) {
        this.targetClass = targetClass;
        this.injectionElements = injectionElements;
        this.elementMemberNames = elementMemberNames;
    }

    public InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz) {

        InjectionMetadata injectionMetadata = buildAutowiringMetadata(clazz);
        return injectionMetadata;
    }

    public InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
        if (clazz == null) {
            return null;
        }

        Class<?> targetClass = clazz;


        List<InjectionElement> currElements = new ArrayList<>();
        List<String> memberNames = new ArrayList<>();

        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Autowired.class)) {
                InjectionElement injectionElement=new InjectionElement(field, true,false,Autowired.class.getSimpleName());
                injectionElement.setAutowiredMeta(field.getAnnotation(Autowired.class));
                currElements.add(injectionElement);
                memberNames.add(field.getName());
            }
            if (field.isAnnotationPresent(AutoValue.class)) {
                InjectionElement injectionElement=new InjectionElement(field, true,false,AutoValue.class.getSimpleName());
                injectionElement.setAutoValueMeta(field.getAnnotation(AutoValue.class));
                currElements.add(injectionElement);
                memberNames.add(field.getName());
            }
        }

        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Autowired.class)) {
                currElements.add(new InjectionElement(method, false,true,Autowired.class.getSimpleName()));
                memberNames.add(method.getName());
            }
            if (method.isAnnotationPresent(AutoValue.class)) {
                currElements.add(new InjectionElement(method, false,true,AutoValue.class.getSimpleName()));
                memberNames.add(method.getName());
            }
        }


        return new InjectionMetadata(targetClass, currElements, memberNames);
    }

    public List<String> getElementMemberNames() {
        return elementMemberNames;
    }

    public Collection<InjectionElement> getInjectionElements() {
        if (injectionElements == null) {
            return Collections.emptyList();
        }
        return injectionElements;
    }

    public void injectElements(Object target, String beanName, Map<String, Object> elementMemberValues) throws Exception {
        Collection<InjectionElement> elementsToIterate = this.injectionElements;
        if (!elementsToIterate.isEmpty()) {
            for (InjectionElement element : elementsToIterate) {
                Object memberValue = elementMemberValues.get(element.getMember().getName());
                element.inject(target, beanName, memberValue);
            }
        }
    }

}
